﻿using System;
using System.Collections.Generic;


namespace TrivialPurfuit.Business
{
    public static class BoardMap
    {
        ///////////////////////////////////////////////////////////////////////////////////////////
        // Constants
        ///////////////////////////////////////////////////////////////////////////////////////////
        public const int NULL_SPACE_NUMBER = -1;
        public const int FIRST_SPACE_NUMBER = 0;
        public const int LAST_SPACE_NUMBER = 56;
        public const int BASE1_SPACE_NUMBER = 0;
        public const int BASE2_SPACE_NUMBER = 10;
        public const int BASE3_SPACE_NUMBER = 20;
        public const int BASE4_SPACE_NUMBER = 30;
        public const int CENTER_SPACE_NUMBER = 56;
        public const int OFF_THE_BOARD_DISTANCE = -100;

        public enum SpaceType { Normal, Base, RollAgain, Center };

        ///////////////////////////////////////////////////////////////////////////////////////////
        // Local Variables
        ///////////////////////////////////////////////////////////////////////////////////////////
        private static Dictionary<int, int[]> map;

        private static List<int> destinations;

        ///////////////////////////////////////////////////////////////////////////////////////////
        // Static Functions
        ///////////////////////////////////////////////////////////////////////////////////////////
        public static int getAValidSpace(int value)
        {
            int validValue = NULL_SPACE_NUMBER;

            if (isValidSpace(value))
                validValue = value;

            return validValue;
        }

        public static bool isValidSpace(int value)
        {
            if (value >= FIRST_SPACE_NUMBER && value <= LAST_SPACE_NUMBER)
                return true;

            return false;
        }

        public static SpaceType determineSpaceTypeFromSpaceNumber(int spaceNumber)
        {
            if (!isValidSpace(spaceNumber))
                throw new Exception("Invalid Space Number");

            if (spaceNumber == CENTER_SPACE_NUMBER)
                return SpaceType.Center;

            else if (GameBoard.Instance.Spaces[spaceNumber].Category == GameBoard.Instance.NullCategory)
                return SpaceType.RollAgain;

            else if (spaceNumber == BASE1_SPACE_NUMBER ||
                spaceNumber == BASE2_SPACE_NUMBER ||
                spaceNumber == BASE3_SPACE_NUMBER ||
                spaceNumber == BASE4_SPACE_NUMBER)
                return SpaceType.Base;

            else 
                return SpaceType.Normal;
        }

        public static QuestionCategory determineCategoryFromSpaceNumber(int spaceNumber)
        {
            if (!isValidSpace(spaceNumber))
                throw new Exception("Invalid Space Number");

            return GameBoard.Instance.Spaces[spaceNumber].Category;
        }

        /// <summary>
        /// This fills the dictionary with all the valid paths for moving through the game board.
        /// Essentially it is a list of all the squares and their nieghbors. The key of the dictionary is the 
        /// space number, and the value is an array of the nieghboring space numbers.
        /// </summary>
        private static void buildMap()
        {
            map = new Dictionary<int, int[]>();

            // Top edge clockwise
            map.Add(0, new int[] { 1, 39 });
            map.Add(1, new int[] { 0, 2 });
            map.Add(2, new int[] { 1, 3 });
            map.Add(3, new int[] { 2, 4 });
            map.Add(4, new int[] { 3, 5 });
            map.Add(5, new int[] { 4, 6, 40 });
            map.Add(6, new int[] { 5, 7 });
            map.Add(7, new int[] { 6, 8 });
            map.Add(8, new int[] { 7, 9 });
            map.Add(9, new int[] { 8, 10 });
            map.Add(10, new int[] { 9, 11 });

            // Right edge clockwise
            map.Add(11, new int[] { 10, 12 });
            map.Add(12, new int[] { 11, 13 });
            map.Add(13, new int[] { 12, 14 });
            map.Add(14, new int[] { 13, 15 });
            map.Add(15, new int[] { 14, 16, 44 });
            map.Add(16, new int[] { 15, 17 });
            map.Add(17, new int[] { 16, 18 });
            map.Add(18, new int[] { 17, 19 });
            map.Add(19, new int[] { 18, 20 });

            //Bottom Edge clockwise
            map.Add(20, new int[] { 19, 21 });
            map.Add(21, new int[] { 20, 22 });
            map.Add(22, new int[] { 21, 23 });
            map.Add(23, new int[] { 22, 24 });
            map.Add(24, new int[] { 23, 25 });
            map.Add(25, new int[] { 24, 26, 48 });
            map.Add(26, new int[] { 25, 27 });
            map.Add(27, new int[] { 26, 28 });
            map.Add(28, new int[] { 27, 29 });
            map.Add(29, new int[] { 28, 30 });
            map.Add(30, new int[] { 29, 31 });

            //Left Edge clockwise
            map.Add(31, new int[] { 30, 32 });
            map.Add(32, new int[] { 31, 33 });
            map.Add(33, new int[] { 32, 34 });
            map.Add(34, new int[] { 33, 35 });
            map.Add(35, new int[] { 34, 36, 52 });
            map.Add(36, new int[] { 35, 37 });
            map.Add(37, new int[] { 36, 38 });
            map.Add(38, new int[] { 37, 39 });
            map.Add(39, new int[] { 38, 0 });
            
            //Top spoke going in
            map.Add(40, new int[] { 5, 41 });
            map.Add(41, new int[] { 40, 42 });
            map.Add(42, new int[] { 41, 43 });
            map.Add(43, new int[] { 42, 56 });

            // Rigth spoke going in
            map.Add(44, new int[] { 15, 45 });
            map.Add(45, new int[] { 44, 46 });
            map.Add(46, new int[] { 45, 47 });
            map.Add(47, new int[] { 46, 56 });
            
            // Bottom spoke going in
            map.Add(48, new int[] { 25, 49 });
            map.Add(49, new int[] { 48, 50 });
            map.Add(50, new int[] { 49, 51 });
            map.Add(51, new int[] { 50, 56 });
            
            // Left spoke going in
            map.Add(52, new int[] { 35, 53 });
            map.Add(53, new int[] { 52, 54 });
            map.Add(54, new int[] { 53, 55 });
            map.Add(55, new int[] { 54, 56 });

            // Center
            map.Add(56, new int[] { 43, 47, 51, 55 });
            
        }

        public static List<int> getPossibleDestinations(GamePiece piece, int availableMoves)
        {
            const int NO_PREVIOUS_SPACE = NULL_SPACE_NUMBER;

            if (map == null)
                buildMap();

            destinations = new List<int>();

            recusivelyStepThroughMap(NO_PREVIOUS_SPACE, piece.SpaceNumber, availableMoves);

            return destinations;
        }


        private static void recusivelyStepThroughMap(int previousSpace, int thisSpace, int availableMoves)
        {
            int[] neighbors;

            if (availableMoves == 0)
                destinations.Add(thisSpace);

            else if (map.TryGetValue(thisSpace, out neighbors))
            {
                foreach (int neighbor in neighbors)
                {
                    if (neighbor == previousSpace)
                        continue;
                    else
                        recusivelyStepThroughMap(thisSpace, neighbor, availableMoves - 1);
                }
            }
            else
            {
                throw new IndexOutOfRangeException("Recursively stepping out of bounds.");
            }
        }


    }
}
